define(function(require, exports) {
	
	var doc = (self.frameElement && self.frameElement.tagName == "IFRAME") ? window.parent.document : document;
	var win = (self.frameElement && self.frameElement.tagName == "IFRAME") ? window.parent : window;
	
	// Position
	// --------
	// 定位工具组件，将一个 DOM 节点相对对另一个 DOM 节点进行定位操作。
	// 代码易改，人生难得
	var Position = exports, VIEWPORT = {
		_id : "VIEWPORT",
		nodeType : 1
	}, $ = require("jquery"), isPinFixed = false, ua = (window.navigator.userAgent || "").toLowerCase(), isIE6 = ua.indexOf("msie 6") !== -1;
	// 将目标元素相对于基准元素进行定位
	// 这是 Position 的基础方法，接收两个参数，分别描述了目标元素和基准元素的定位点
	Position.pin = function(pinObject, baseObject) {
		// 将两个参数转换成标准定位对象 { element: a, x: 0, y: 0 }
		pinObject = normalize(pinObject);
		baseObject = normalize(baseObject);
		// 设定目标元素的 position 为绝对定位
		// 若元素的初始 position 不为 absolute，会影响元素的 display、宽高等属性
		var pinElement = $(pinObject.element);
		if (pinElement.css("position") !== "fixed" || isIE6) {
			pinElement.css("position", "absolute");
			isPinFixed = false;
		} else {
			// 定位 fixed 元素的标志位，下面有特殊处理
			isPinFixed = true;
		}
		// 将位置属性归一化为数值
		// 注：必须放在上面这句 `css('position', 'absolute')` 之后，
		// 否则获取的宽高有可能不对
		posConverter(pinObject);
		posConverter(baseObject);
		var parentOffset = getParentOffset(pinElement);
		var baseOffset = baseObject.offset();
		// 计算目标元素的位置
		var top = baseOffset.top + baseObject.y - pinObject.y - parentOffset.top;
		var left = baseOffset.left + baseObject.x - pinObject.x - parentOffset.left;
		
		// 定位目标元素
		pinElement.css({
			left : left,
			top : top
		});
	};
	// 将目标元素相对于基准元素进行居中定位
	// 接受两个参数，分别为目标元素和定位的基准元素，都是 DOM 节点类型
	Position.center = function(pinElement, baseElement) {
		Position.pin({
			element : pinElement,
			x : "50%",
			y : "50%"
		}, {
			element : baseElement,
			x : "50%",
			y : "50%"
		});
	};
	// 这是当前可视区域的伪 DOM 节点
	// 需要相对于当前可视区域定位时，可传入此对象作为 element 参数
	Position.VIEWPORT = VIEWPORT;
	// Helpers
	// -------
	// 将参数包装成标准的定位对象，形似 { element: a, x: 0, y: 0 }
	function normalize(posObject) {
		posObject = toElement(posObject) || {};
		if (posObject.nodeType) {
			posObject = {
				element : posObject
			};
		}
		var element = toElement(posObject.element) || VIEWPORT;
		if (element.nodeType !== 1) {
			throw new Error("posObject.element is invalid.");
		}
		var result = {
			element : element,
			x : posObject.x || 0,
			y : posObject.y || 0
		};
		// config 的深度克隆会替换掉 Position.VIEWPORT, 导致直接比较为 false
		var isVIEWPORT = element === VIEWPORT || element._id === "VIEWPORT";
		// 归一化 offset
		result.offset = function() {
			// 若定位 fixed 元素，则父元素的 offset 没有意义
			if (isPinFixed) {
				return {
					left : 0,
					top : 0
				};
			} else if (isVIEWPORT) {
				return {
					left : $(doc).scrollLeft(),
					top : $(doc).scrollTop()
				};
			} else {
				return getOffset($(element)[0]);
			}
		};
		// 归一化 size, 含 padding 和 border
		result.size = function() {
			var el = isVIEWPORT ? $(win) : $(element);
			return {
				width : el.outerWidth(),
				height : el.outerHeight()
			};
		};
		return result;
	}
	// 对 x, y 两个参数为 left|center|right|%|px 时的处理，全部处理为纯数字
	function posConverter(pinObject) {
		pinObject.x = xyConverter(pinObject.x, pinObject, "width");
		pinObject.y = xyConverter(pinObject.y, pinObject, "height");
	}
	// 处理 x, y 值，都转化为数字
	function xyConverter(x, pinObject, type) {
		// 先转成字符串再说！好处理
		x = x + "";
		// 处理 px
		x = x.replace(/px/gi, "");
		// 处理 alias
		if (/\D/.test(x)) {
			x = x.replace(/(?:top|left)/gi, "0%").replace(/center/gi, "50%").replace(/(?:bottom|right)/gi, "100%");
		}
		// 将百分比转为像素值
		if (x.indexOf("%") !== -1) {
			// 支持小数
			x = x.replace(/(\d+(?:\.\d+)?)%/gi, function(m, d) {
				return pinObject.size()[type] * (d / 100);
			});
		}
		// 处理类似 100%+20px 的情况
		if (/[+\-*\/]/.test(x)) {
			try {
				// eval 会影响压缩
				// new Function 方法效率高于 for 循环拆字符串的方法
				// 参照：http://jsperf.com/eval-newfunction-for
				x = new Function("return " + x)();
			} catch (e) {
				throw new Error("Invalid position value: " + x);
			}
		}
		// 转回为数字
		return numberize(x);
	}
	// 获取 offsetParent 的位置
	function getParentOffset(element) {
		var parent = element.offsetParent();
		// IE7 下，body 子节点的 offsetParent 为 html 元素，其 offset 为
		// { top: 2, left: 2 }，会导致定位差 2 像素，所以这里将 parent
		// 转为 document.body
		if (parent[0] === doc.documentElement) {
			parent = $(doc.body);
		}
		// 修正 ie6 下 absolute 定位不准的 bug
		if (isIE6) {
			parent.css("zoom", 1);
		}
		// 获取 offsetParent 的 offset
		var offset;
		// 当 offsetParent 为 body，
		// 而且 body 的 position 是 static 时
		// 元素并不按照 body 来定位，而是按 document 定位
		// http://jsfiddle.net/afc163/hN9Tc/2/
		// 因此这里的偏移值直接设为 0 0
		if (parent[0] === doc.body && parent.css("position") === "static") {
			offset = {
				top : 0,
				left : 0
			};
		} else {
			offset = getOffset(parent[0]);
		}
		// 根据基准元素 offsetParent 的 border 宽度，来修正 offsetParent 的基准位置
		offset.top += numberize(parent.css("border-top-width"));
		offset.left += numberize(parent.css("border-left-width"));
		return offset;
	}
	function numberize(s) {
		return parseFloat(s, 10) || 0;
	}
	function toElement(element) {
		return $(element)[0];
	}
	// fix jQuery 1.7.2 offset
	// document.body 的 position 是 absolute 或 relative 时
	// jQuery.offset 方法无法正确获取 body 的偏移值
	// -> http://jsfiddle.net/afc163/gMAcp/
	// jQuery 1.9.1 已经修正了这个问题
	// -> http://jsfiddle.net/afc163/gMAcp/1/
	// 这里先实现一份
	// 参照 kissy 和 jquery 1.9.1
	// ->
	// https://github.com/kissyteam/kissy/blob/master/src/dom/sub-modules/base/src/base/offset.js#L366
	// -> https://github.com/jquery/jquery/blob/1.9.1/src/offset.js#L28
	function getOffset(element) {
		var box = element.getBoundingClientRect(), docElem = doc.documentElement;
		// < ie8 不支持 win.pageXOffset, 则使用 docElem.scrollLeft
		return {
			left : box.left + (win.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || doc.body.clientLeft || 0),
			top : box.top + (win.pageYOffset || docElem.scrollTop) - (docElem.clientTop || doc.body.clientTop || 0)
		};
	}
});